package com.oa.log.aspect;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import com.oa.log.annotation.Log;
import com.oa.log.entity.LogEntity;
import com.oa.log.service.LogService;
import com.oa.user.dto.UserInfoDto;
import com.oa.util.SessionUtil;

@Aspect
@Component
public class LogAspect {
	@Resource
	private HttpServletRequest request;
	@Resource
	private LogService logService;

	private ThreadLocal<LogEntity> logEntity = new ThreadLocal<>();
	private ThreadLocal<UserInfoDto> userinfo = new ThreadLocal<>();

	private ExecutorService executorService;

	@Pointcut("@annotation(com.oa.log.annotation.Log)")
	private void log() {
	}

	@Before("log()")
	public void doBefore(JoinPoint joinPoint) {
		HttpSession session = request.getSession();
		userinfo.set(SessionUtil.getUserInfo(session));
		executorService = Executors.newCachedThreadPool();
	}

	@After("@annotation(log)")
	public void doAfter(JoinPoint joinPoint, Log log) {
		UserInfoDto info = userinfo.get();

		if (info != null) {
			Date date = new Date();
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
			String now = simpleDateFormat.format(date);

			System.out.println("FullName: " + info.getFullname());
			System.out.println("Time: " + now);
			System.out.println("Oprate: " + log.oprate());
			System.out.println("LocalIP: " + request.getLocalAddr());
			System.out.println("RemoteIP: " + request.getRemoteAddr());

			LogEntity entity = new LogEntity();
			entity.setUserId(info.getId());
			entity.setOprate(log.oprate());
			entity.setOprateTime(date);
			entity.setIp(request.getLocalAddr());
			entity.setSuccess(true);

			Future<Long> future = executorService.submit(new LogSaveThread(entity, logService));

			try {
				entity.setId(future.get());
				logEntity.set(entity);
			} catch (InterruptedException | ExecutionException e) {
				e.printStackTrace();
			}
		}

	}

	@AfterThrowing(pointcut = "log()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		System.out.println(e.getMessage());

		LogEntity entity = logEntity.get();
		entity.setSuccess(false);

		executorService.execute(new LogUpdateThread(entity, logService));
	}

	public class LogSaveThread implements Callable<Long> {
		private LogEntity entity;
		private LogService logService;

		public LogSaveThread(LogEntity entity, LogService logService) {
			this.entity = entity;
			this.logService = logService;
		}

		@Override
		public Long call() throws Exception {
			return this.logService.save(this.entity).getId();
		}
	}

	public class LogUpdateThread implements Runnable {
		private LogEntity entity;
		private LogService logService;

		public LogUpdateThread(LogEntity entity, LogService logService) {
			this.entity = entity;
			this.logService = logService;
		}

		@Override
		public void run() {
			this.logService.save(this.entity);
		}
	}

}
